import sys
import logging
import locale
import codecs
import time
import libxml2
import yaml
import wx

from pyxmpp.client import Client
from pyxmpp.stream import Stream
from pyxmpp.streambase import StreamError
from pyxmpp.jid import JID
from pyxmpp.iq import Iq
from pyxmpp.roster import Roster
from pyxmpp.jabber.client import JabberClient
from pyxmpp.message import Message
from pyxmpp.presence import Presence

# XMPP protocol is Unicode-based to properly display data received _must_ convert it to local encoding or UnicodeException may be raised
locale.setlocale(locale.LC_CTYPE, "")
encoding = locale.getlocale()[1]
if not encoding:
    encoding = "us-ascii"
# set stdout en stderr encoding as well
sys.stdout = codecs.getwriter(encoding)(sys.stdout, errors = "replace")
sys.stderr = codecs.getwriter(encoding)(sys.stderr, errors = "replace")

#get the logger instance for this app
logger = logging.getLogger('provinci')

class ProVinciImClient(JabberClient):
    namespace = 'http://www.kunnis.net/provinci'

    def __init__(self, parent, **kwargs):
        JabberClient.__init__(self, **kwargs)
        self.parent = parent
    
    def sendMessage(self, to, body, subject=None):
        logger.debug("sendMessage: to: " + str(to) + ' - ' + str(body))
        if not self.stream:
            raise Exception('not connected, please connect first')
        if not self.session_established:
            self.request_session()
        timeout = 10 #seconds
        while not self.session_established:
            if timeout > 0:
                time.sleep(1)
                timeout = timeout - 1
            else:
                logger.debug('No session established. Unable to send msg')
                return  
        msg = Message(from_jid=self.jid, to_jid=JID(to),body=body,subject=subject)
        msg.set_type('chat')
        self.stream.send(msg)                
    
    def loop(self):
        logger.debug("start looping")
        self.running = True
        try:
            while self.running:
                self.stream.loop_iter(1)
            logger.debug("loop stopped")
        except (StreamError),e:
            logger.error(repr(e))
                      
    def exit(self):
        logger.debug("IM exit")
        self.running = False
        self.disconnect()
        
    def printRosterItem(self,item):
        if item.name:
            name=item.name
        else:
            name=u""
        logger.debug(u'%s "%s" subscription=%s groups=%s'
                % (unicode(item.jid), name, item.subscription,
                    u",".join(item.groups)) )
                
    def addToRoster(self):
        JabberClient.addToRoster(self)
        logger.debug("addToRoster")
        if not self.roster == None:
            logger.debug('roster: ' + str(self.roster))
        else:
            logger.debug('roster not available')          
##        self.roster.add_item('testuser3@jabber.ambrero.nl')
    
    def subscribe(self, to_jid):
        self.stream.send(Presence(to_jid=to_jid, stanza_type='subscribe'))        
    
    def setStatus(self, **kwargs):
        logger.debug('setting status to: ' + str(kwargs['stanza_type']))
        self.stream.send(Presence(**kwargs))
    
    def sendGetIq(self, toJid, resource, queryMsg):
        ''' 
        Disadvantage: You need to know full destination JID (incl the resource part) to send IQ stanzas to other client. 
        Generally that means presence tracking, so you know what resources of the destination account are connected.
        <message/> stanzas are sent to the highest priority resource if sent to bare JID (without resource).
        '''
        logger.debug('sendGetIq')
        iq = Iq(from_jid=self.jid, to_jid=toJid, stanza_type='get')
        queryNode = iq.new_query(ns_uri=self.namespace)
        yamlNode = libxml2.newNode('yaml')
        yamlNode.setContent(queryMsg)
        queryNode.addChild(yamlNode)
        logger.debug('OUT: IQ: ' + str(iq.get_node()))        
        self.stream.set_response_handlers(iq,self.onIqResult, self.onIqError, self.onIqTimeout)  
        self.stream.send(iq)      

    
    #---Handlers----------------------------------------------------------------------------
    
    def onMessage(self, stanza):
        body = stanza.get_body()
        logger.debug('onMessage: %s: \n%s' % (JID(stanza.get_from_jid()).node, body))
        if body.startswith('!!python/object:request'):
            self.onRequest(body)
        else:
            self.parent.onMessage(stanza)

    def onPresence(self, presence):
        show = presence.get_show()
        status = presence.get_status()
        logger.debug('onPresence: %s: %s' % (show, status))
    
    def onIqGet(self, iqStanza):        
        logger.debug("onIqGet: iqStanza: " + str(iqStanza.get_query()))
        
    def onIqSet(self, iqStanza):
        logger.debug("onIqSet: iqStanza: " + str(iqStanza.get_query()))
        
    def onIqResult(self, iqStanza):
        logger.debug("onIqResult: iqStanza: " + str(iqStanza.get_query()))
        
    def onIqError(self, iqStanza):
        logger.debug("onIqError: iqStanza: " + str(iqStanza.get_query()))
        
    def onIqTimeout(self, iqStanza):
        logger.debug("onIqTimeout: iqStanza: " + str(iqStanza.get_query()))
                
    def onRequest(self, body):
        logger.debug('onRequest')
        if body.startswith('!!python/object:request.Request'):
            request = yaml.load(body)
            logger.debug('request: ' + str(request))
            wx.CallAfter(lambda x: self.parent.onRequest(x), request)
        elif body.startswith('!!python/object:request.Response'):
            response = yaml.load(body)
            logger.debug('response: ' + str(response))
            wx.CallAfter(lambda x: self.parent.onResponse(x), response)

    #---Overridden handlers----------------------------------------------------------------------------

    def session_started(self):
        logger.debug('session started')
        self.stream.set_message_handler('normal',self.onMessage)
        self.stream.set_presence_handler(typ=None, handler=self.onPresence)
        self.stream.set_iq_get_handler('query', self.namespace, self.onIqGet)
        self.stream.set_iq_set_handler('query', self.namespace, self.onIqSet)
        self.parent.sessionStarted()

    def connected(self):
        logger.debug("connected")        
        self.parent.connected()

    def idle(self):
        logger.debug("idle")
        Client.idle(self)
##        if self.session_established:
##            target=JID("jajcus",s.jid.domain)
##            self.stream.send(Message(to_jid=target,body=unicode("bladiebla","utf-8")))
##            im.sendMessage('testuser3@jabber.ambrero.nl', 'hibody', 'hisubject')

    def post_disconnect(self):
        logger.debug("Disconnected")
        raise Disconnected

    def process_registration_form(self, stanza, form):
        logger.debug("process_registration_form")
        logger.debug("stanza:" + str(stanza.get_node()))
        logger.debug("form:" + str(form.as_xml()))
        for field in form:
            logger.debug("field: " + str(field.name))
        form['username'].value = username
        form['password'].value = password
        self.submit_registration_form(form)
    
    def stream_state_changed(self, state, arg):
        logger.debug("stream_state_changed:" + state)
        
    def stream_error(self, err):
        logger.debug("stream_error:" + err.as_xml())
        
    def authorized(self):
        logger.debug("authorized")
        
    def roster_updated(self, item=None):
        logger.debug("roster_updated: roster item: " + str(item))        
        #if item==None the whole roster was received
        if not item:
            logger.debug('Roster:')
            for item in self.roster.get_items():
                self.printRosterItem(item)
        else:
            self.printRosterItem(item)
        
